#include <mcp/types.h>
#include <mcp/json/value.h>
#include <mcp/error.h>
#include <iostream>
#include <sstream>
#include <chrono>
#include <algorithm>
#include <string>

namespace mcp {
namespace types {

// ContentType to string conversion
std::string content_type_to_string(ContentType type) {
    switch (type) {
        case ContentType::Text: return "text";
        case ContentType::Image: return "image";
        case ContentType::EmbeddedResource: return "resource";
        case ContentType::Audio: return "audio";
        case ContentType::Video: return "video";
        default: return "unknown";
    }
}

// ContentType from string conversion
ContentType string_to_content_type(const std::string& str) {
    if (str == "text") return ContentType::Text;
    if (str == "image") return ContentType::Image;
    if (str == "resource") return ContentType::EmbeddedResource;
    if (str == "audio") return ContentType::Audio;
    if (str == "video") return ContentType::Video;
    return ContentType::Text; // Default
}

// LogLevel to string conversion
std::string log_level_to_string(LogLevel level) {
    switch (level) {
        case LogLevel::Debug: return "debug";
        case LogLevel::Info: return "info";
        case LogLevel::Warning: return "warning";
        case LogLevel::Error: return "error";
        default: return "info";
    }
}

// LogLevel from string conversion
LogLevel string_to_log_level(const std::string& str) {
    if (str == "debug") return LogLevel::Debug;
    if (str == "info") return LogLevel::Info;
    if (str == "warning") return LogLevel::Warning;
    if (str == "error") return LogLevel::Error;
    return LogLevel::Info; // Default
}

// PromptRole to string conversion
std::string prompt_role_to_string(PromptRole role) {
    switch (role) {
        case PromptRole::User: return "user";
        case PromptRole::Assistant: return "assistant";
        case PromptRole::System: return "system";
        default: return "user";
    }
}

// PromptRole from string conversion
PromptRole string_to_prompt_role(const std::string& str) {
    if (str == "user") return PromptRole::User;
    if (str == "assistant") return PromptRole::Assistant;
    if (str == "system") return PromptRole::System;
    return PromptRole::User; // Default
}

// ContentBlock implementations
ContentBlock::ContentBlock(ContentType type) : type(type) {}

TextContent::TextContent() : ContentBlock(ContentType::Text) {}
TextContent::TextContent(std::string text)
    : ContentBlock(ContentType::Text), text(std::move(text)) {}

ImageContent::ImageContent() : ContentBlock(ContentType::Image) {}
ImageContent::ImageContent(std::string data, std::string mime_type)
    : ContentBlock(ContentType::Image), data(std::move(data)), mime_type(std::move(mime_type)) {}

EmbeddedResource::EmbeddedResource() : ContentBlock(ContentType::EmbeddedResource) {}
EmbeddedResource::EmbeddedResource(Resource resource)
    : ContentBlock(ContentType::EmbeddedResource), resource(std::move(resource)) {}

// Resource operators
bool Resource::operator==(const Resource& other) const {
    return uri == other.uri;
}

// Tool operators
bool Tool::operator==(const Tool& other) const {
    return name == other.name;
}

// ElicitationResult implementation
std::string elicitation_action_to_string(ElicitationResult::Action action) {
    switch (action) {
        case ElicitationResult::Action::Accept: return "accept";
        case ElicitationResult::Action::Decline: return "decline";
        case ElicitationResult::Action::Cancel: return "cancel";
        default: return "unknown";
    }
}

ElicitationResult::Action string_to_elicitation_action(const std::string& str) {
    if (str == "accept") return ElicitationResult::Action::Accept;
    if (str == "decline") return ElicitationResult::Action::Decline;
    if (str == "cancel") return ElicitationResult::Action::Cancel;
    return ElicitationResult::Action::Cancel; // Default
}

// LoggingMessage implementation
LoggingMessage::LoggingMessage(LogLevel level, std::string message)
    : level(level), message(std::move(message)) {
    timestamp = std::chrono::system_clock::now();
}

// Utility functions for JSON conversion
json::Value content_block_to_json(const std::shared_ptr<ContentBlock>& block) {
    if (!block) {
        return json::Value();
    }

    std::map<std::string, json::Value> map_data;
    map_data["type"] = json::Value(content_type_to_string(block->type));
    json::Value result = json::Value(map_data);

    switch (block->type) {
        case ContentType::Text: {
            auto text = std::static_pointer_cast<TextContent>(block);
            result["text"] = json::Value(text->text);
            break;
        }
        case ContentType::Image: {
            auto image = std::static_pointer_cast<ImageContent>(block);
            result["data"] = json::Value(image->data);
            result["mime_type"] = json::Value(image->mime_type);
            break;
        }
        case ContentType::EmbeddedResource: {
            auto resource = std::static_pointer_cast<EmbeddedResource>(block);
            std::map<std::string, json::Value> resource_map;
            resource_map["uri"] = json::Value(resource->resource.uri);
            resource_map["name"] = json::Value(resource->resource.name);
            resource_map["mime_type"] = json::Value(resource->resource.mime_type);
            result["resource"] = json::Value(resource_map);
            if (resource->resource.description) {
                result["resource"]["description"] = json::Value(*resource->resource.description);
            }
            break;
        }
        default:
            break;
    }

    return result;
}

std::shared_ptr<ContentBlock> json_to_content_block(const json::Value& json) {
    if (!json.is_object() || !json.contains("type")) {
        return nullptr;
    }

    std::string type_str = json["type"].as_string();
    ContentType type = string_to_content_type(type_str);

    switch (type) {
        case ContentType::Text:
            if (json.contains("text")) {
                return std::make_shared<TextContent>(json["text"].as_string());
            }
            break;
        case ContentType::Image:
            if (json.contains("data") && json.contains("mime_type")) {
                return std::make_shared<ImageContent>(
                    json["data"].as_string(),
                    json["mime_type"].as_string()
                );
            }
            break;
        case ContentType::EmbeddedResource:
            if (json.contains("resource")) {
                const auto& resource_json = json["resource"];
                Resource resource;
                resource.uri = resource_json["uri"].as_string();
                resource.name = resource_json["name"].as_string();
                resource.mime_type = resource_json.contains("mime_type") ? resource_json["mime_type"].as_string() : "text/plain";
                if (resource_json.contains("description")) {
                    resource.description = resource_json["description"].as_string();
                }
                return std::make_shared<EmbeddedResource>(std::move(resource));
            }
            break;
        default:
            break;
    }

    return nullptr;
}


// PromptMessage to JSON conversion
json::Value prompt_message_to_json(const PromptMessage& message) {
    std::map<std::string, json::Value> map_data;
    map_data["role"] = json::Value(mcp::types::prompt_role_to_string(message.role));
    map_data["content"] = content_block_to_json(message.content);
    return json::Value(map_data);
}

mcp::json::optional<PromptMessage> json_to_prompt_message(const json::Value& json) {
    if (!json.is_object() || !json.contains("role") || !json.contains("content")) {
        return mcp::json::optional<PromptMessage>();
    }

    PromptRole role = mcp::types::string_to_prompt_role(json["role"].as_string());
    auto content = json_to_content_block(json["content"]);

    if (!content) {
        return mcp::json::optional<PromptMessage>();
    }

    return PromptMessage{role, content};
}

// SamplingMessage to JSON conversion
json::Value sampling_message_to_json(const SamplingMessage& message) {
    std::map<std::string, json::Value> map_data;
    map_data["role"] = json::Value(mcp::types::prompt_role_to_string(message.role));
    map_data["content"] = content_block_to_json(message.content);
    return json::Value(map_data);
}

mcp::json::optional<mcp::types::SamplingMessage> json_to_sampling_message(const json::Value& json) {
    if (!json.is_object() || !json.contains("role") || !json.contains("content")) {
        return mcp::json::optional<mcp::types::SamplingMessage>();
    }

    PromptRole role = mcp::types::string_to_prompt_role(json["role"].as_string());
    auto content = json_to_content_block(json["content"]);

    if (!content) {
        return mcp::json::optional<mcp::types::SamplingMessage>();
    }

    return mcp::types::SamplingMessage{role, content};
}

// ProgressNotification to JSON conversion
json::Value progress_notification_to_json(const mcp::types::ProgressNotification& notification) {
    json::Value result = json::Value(std::map<std::string, json::Value>());
    result["token"] = json::Value(notification.token);
    result["progress"] = json::Value(notification.progress);

    if (notification.total) {
        result["total"] = json::Value(*notification.total);
    }
    if (notification.message) {
        result["message"] = json::Value(*notification.message);
    }

    return result;
}

// Validation helpers
bool validate_uri(const std::string& uri) {
    if (uri.empty()) {
        return false;
    }

    // Basic URI validation
    // Must contain a scheme and path
    size_t colon_pos = uri.find(':');
    if (colon_pos == std::string::npos || colon_pos == 0) {
        return false;
    }

    std::string scheme = uri.substr(0, colon_pos);
    if (scheme.empty()) {
        return false;
    }

    // Check if there's something after the scheme
    if (uri.size() <= colon_pos + 1) {
        return false;
    }

    return true;
}

bool validate_mime_type(const std::string& mime_type) {
    if (mime_type.empty()) {
        return false;
    }

    // Basic MIME type validation: type/subtype
    size_t slash_pos = mime_type.find('/');
    if (slash_pos == std::string::npos || slash_pos == 0 || slash_pos == mime_type.size() - 1) {
        return false;
    }

    return true;
}

// String helpers
std::string trim_string(const std::string& str) {
    size_t start = str.find_first_not_of(" \t\n\r");
    if (start == std::string::npos) {
        return "";
    }

    size_t end = str.find_last_not_of(" \t\n\r");
    return str.substr(start, end - start + 1);
}

std::vector<std::string> split_string(const std::string& str, char delimiter) {
    std::vector<std::string> result;
    std::istringstream ss(str);
    std::string item;

    while (std::getline(ss, item, delimiter)) {
        result.push_back(trim_string(item));
    }

    return result;
}

} // namespace types
} // namespace mcp